/**
 * 
 */
package org.rollinitiative.d20.entity;

import java.util.Hashtable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rollinitiative.d20.AdjustableValue;
import org.rollinitiative.d20.Adjustment;
import org.rollinitiative.d20.AdjustmentLimiter;
import org.rollinitiative.d20.AdjustmentListener;

/**
 * The AbilityListenerValue class extends AdjustableValue to apply ability modifiers its the
 * baseValue. An example would be saving throws, whose base value is determined by class and level
 * but are modified by an ability modifier score.
 * 
 * @author bebopjmm
 * 
 */
public class AbilityListenerValue extends AdjustableValue
{
    static final Log LOG_ = LogFactory.getLog(AbilityListenerValue.class);


    Hashtable<Ability, Adjustment> abilityModTable_ = new Hashtable<Ability, Adjustment>();
    Hashtable<Ability, AdjustmentLimiter> limits_ = new Hashtable<Ability, AdjustmentLimiter>();


    public AbilityListenerValue(int baseValue, AbilityValue ability)
    {
        super(baseValue);
        addModifier(ability);
    }


    public synchronized void addModifier(AbilityValue ability)
    {
        if (!abilityModTable_.containsKey(ability.getAbility())) {
            Adjustment abilityMod = ability.getModifier();   
            abilityModTable_.put(ability.getAbility(), abilityMod);
            addAdjustment(abilityMod);
        }
        else {
            LOG_.warn("Ignoring attempt to add modifier for AbilityValue already associated: "
                    + ability.getAbility());
        }
    }
    
    public synchronized void removeModifier (AbilityValue ability)
    {
        if (abilityModTable_.containsKey(ability.getAbility())) {
            Adjustment abilityMod = abilityModTable_.remove(ability.getAbility());
            removeAdjustment(abilityMod);
        }
        else {
            LOG_.warn("Ignoring attempt to remove modifier for AbilityValue not associated: " + ability.getAbility());
        }
    }
    
    public synchronized void setLimiter(Ability ability, int modifierLimit)
    {
        if (abilityModTable_.containsKey(ability)) {
            Adjustment abilityMod = abilityModTable_.get(ability);
            removeAdjustment(abilityMod);
            AdjustmentLimiter limiter = new AdjustmentLimiter(abilityMod, modifierLimit);
            addAdjustment(limiter.getLimitedAdjustment());
            limits_.put(ability, limiter);
        }
        else {
            LOG_.warn("Ignoring attempt to limit modifier for AbilityValue not associated: " + ability);
        }
    }
    
    public synchronized void removeLimiter(Ability ability)
    {
        if (limits_.containsKey(ability)) {
            AdjustmentLimiter limit = limits_.remove(ability);
            Adjustment abilityMod = abilityModTable_.get(ability);         
            removeAdjustment(limit.getLimitedAdjustment());
            addAdjustment(abilityMod);
            limit.clear();            
        }
        else {
            LOG_.warn("Ignoring attempt to remove modifier limt for AbilityValue not limited: " + ability);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.rollinitiative.d20.AdjustmentListener#valueChanged(org.rollinitiative.d20.Adjustment)
     */
    @Override
    public synchronized void valueChanged(Adjustment adjustment)
    {
        recalcValue();
    }

}
